/**
 * @brief modbus采集器配置文件接口。文件中记录了modbus采集的任务的描述
 * @version 1.0
 * @date 2018-9-26
 * @author 宋炜
 */

#ifndef __MODBUS_SETTING_HPP__
#define __MODBUS_SETTING_HPP__

#include <string>
#include <vector>
#include <atomic>

#include "cconffile.h"
#include "config.hpp"
#include "misc.h"

#if ENABLE_MODBUS == 1

// 定义MODBUS 协议中各个含义字段的位置。
#define         MODBUS_SITE       0
#define         MODBUS_CMD        1
#define         MODBUS_REG_H      2
#define         MODBUS_REG_L      3
#define         MODBUS_COUNT_H    4
#define         MODBUS_COUNT_L    5
#define         MODBUS_CRC_H      6
#define         MODBUS_CRC_L      7

class ModbusSetting
{
public:
	enum err_code{
		ERR_BASE = -1000,
		ERR_FILE_NAME_EMPTY,            // 文件名空
		ERR_ALLOC_MEM,
		ERR_CAN_NOT_FIND_MISSION,
		ERR_INIT_MISSION_FILE,          // 初始化任务文件失败
		ERR_PREPARE_MISSION_FILE,       // 准备生成任务文件失败
		ERR_ADD_DESC_NODES,
		ERR_ADD_MISSION,
		ERR_ADD_MISSION_DIR,
		OK = 0
	};
	struct stItem{
		int             id;             // 任务ID
		int             opt;            // 操作指令
		int             site;           // 站点编号
		int             start_reg;      // 开始  
		int             count;          // 要采集的寄存器数量
		long            interval;       // 采集周期,单位为毫秒
		int             cmd_len;        // 命令串的长度
		char            cmd[ 20 ];      // 命令串，在设置参数的同时计算生成MODBUS控制串
		/**
		 * @brief 构造函数。默认构造函数会将id , site , start_reg , count , inteval初始化为-1。因此判断一个项目是否是有效
		 * 项目可以通过这个特征判断
		 */
		stItem()
		{
			id          = -1;
			opt         = 0x03;
			site        = -1;
			start_reg   = -1;
			count       = -1;
			interval    = -1;
			cmd_len     = -1;
			memset( cmd , 0 , 20 );
		}

		stItem( int id , int operation , int site , int reg , int count , long intval )
		{
			this->id        = id;
			opt             = operation;
			this->site      = site;
			this->start_reg = reg;
			this->count     = count;
			this->interval  = intval;
			memset( cmd , 0 , 20 );

			make_cmd();
		}
        
		stItem( const stItem & b )
		{
			id          = b.id;
			opt         = b.opt;
			site        = b.site;
			start_reg   = b.start_reg;
			count       = b.count;
			interval    = b.interval;
			cmd_len     = b.cmd_len;

			memcpy( cmd , b.cmd , 8 );
		}
		~stItem(){}

		struct stItem & operator=( const stItem& b)
		{
			id          = b.id;
			opt         = b.opt;
			site        = b.site;
			start_reg   = b.start_reg;
			count       = b.count;
			interval    = b.interval;
			cmd_len     = b.cmd_len;
			
			memcpy( cmd , b.cmd , 8 );
			return *this;
		}

		bool operator==( const stItem& b )
		{
			return  site        == b.site       &&
				opt         == b.opt        &&
				start_reg   == b.start_reg  &&
				count       == b.count      &&
				interval    == b.interval;
		}
		/**
		 * @brief 判断当前项目是否合法。
		 * @return 当前项目合法则返回true; 否则返回false.
		 */
		operator bool() const{ return id == -1 ? false : true; }
		/**
		 * @brief 设置参数
		 * @param id
		 * @param operation
		 * @param site
		 * @param reg
		 * @param count
		 * @param intval
		 */
		void operator ()( int id , int operation , int site , int reg , int count , long intval )
		{
			this->id        = id;
			opt             = operation;
			this->site      = site;
			this->start_reg = reg;
			this->count     = count;
			this->interval  = intval;

			make_cmd();
		}
		/**
		 * @brief 设置或者读取站点编号
		 */
		void Site( int s ){ site = s; make_cmd();}
		int Site( ) const { return site; }
		/**
		 * @brief 设置或者读取任务寄存器
		 */
		void Register( int reg ){ start_reg = reg; make_cmd(); }
		int Register() const{ return start_reg; }
		/**
		 * @brief 设置或者读取采集目标的寄存器数量
		 */
		void Count( int c ){ count = c; make_cmd(); }
		int Count() const{ return count;}
		/**
		 * @brief 设置或者读取MODBUS采集的间隔
		 */
		void Interval( int itval ){ interval = itval; make_cmd(); }
		int Interval()const { return interval; }

		void make_cmd();
		/**
		 * @brief 拷贝命令串。
		 * @param data , 目标内存指针
		 * @param len , 拷贝的长度
		 * @return 成功拷贝返回true，否则返回false;
		 */
		bool CloneCmd( char * data , size_t& len ) const;
    	};

    	typedef std::vector< stItem >     ModMission;
private:
	std::string     m_mission_file;      // 任务列表文件路径      
	int             m_curr_id;           // 当前可以分配的ID

	ModMission      m_data;              // 任务列表

	std::atomic< err_code > m_error;     // 上次错误编号
public:
	ModbusSetting();
	ModbusSetting( const std::string& file );
	virtual ~ModbusSetting();
	/**
	 * @brief 获取任务列表
	 */
	const ModMission & Mission() const;
	/**
	 * @brief 添加任务列表
	 * @param opt , 操作指令，这里只支持读取指令。
	 * @param site , 站点编号
	 * @param reg ， 开始寄存器编号
	 * @param count , 需要采集的寄存器数量
	 * @param interval , 采集间隔时间，单位为ms
	 * @return 成功返回OK，否则返回错误代码
	 */
	err_code Mission( int opt , int site , int reg , int count , long intval );
	/**
	 * @brief 保存文件
	 * @param file
	 */
	err_code Save( const std::string& file );
	/**
	 * @brief 删除指定的任务
	 * @param id
	 */
	err_code Del( int id );
private:
	/**
	 * @brief 从文件中读取记录
	 */
	void init_from_file( const std::string& file );
	/**
	 * @brief 重新调整ID编号，将ID编号调整成连续的
	 */
	void re_alloc_id( );
	/**
	 * @brief 将配置信息转换为XML DOM
	 * @param doc
	 */
	void to_xml( TiXmlDocument& doc );
	/**
	 * @brief 显示错误信息，将错误信息输出在标准输出口上。
	 * @param e
	 */
	void show_err_msg( CConfFile::err_code e );
	/**
	 * @brief 准备XML文件的附加信息内容
	 */
	bool prepare_xml_file( TiXmlDocument& doc );

	/**
	 */
	bool add_file_desc( TiXmlDocument& doc );

	/**
	 * @brief 添加任务描述数据的目录
	 * @param root ， 任务XML文件的根节点
	 * @param i , 目录名称
	 * @return 成功返回目录节点，否则返回nullptr
	 * @excpetion
	 */
	TiXmlElement * add_mission_dir( TiXmlElement * root , int i );

	/**
	 * @brief 添加一条任务
	 * @parm dir 
	 * @param it
	 * @return
	 * @exception
	 */
	bool add_mission( TiXmlElement* dir , ModMission::iterator it );
};
#endif
#endif